/********************************************************/
/* Aluno: Luciano G. S. Ramalho                         */
/* Numero USP: 1002101                                  */
/* Exercicio-Programa 1 -- Maquina Vendedora Automatica */
/* MAC110 (BCC) -- 2007 -- Professor: Reverbel          */
/* Compilador: gcc (GCC) 4.0.3 (Ubuntu 4.0.3-1ubuntu5)  */
/********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define PROD_TIPOS 4

#define QT_VAL_DISTINTOS (sizeof(VAL_NUMERARIO) / sizeof(VAL_NUMERARIO[0]))

#define SEM_TROCO -1

enum comando {
	SAIR=0, 
	CHOCA=-1, MINER=-2, TOBLE=-3, XIRIT=-4, TROCO=-5, /* botoes */
	R10=1000, R5=500, R2=200, R1=100,  /* colocar dinheiro */
	C50=50, C25=25, C10=10, C5=5,      /* colocar moedas */
	ENTRAR=-9 	/* estado inicial */
};

int receber(int saldo, int valor);
int vender(int saldo, enum comando botao);
void exibirCaixa();
void exibirCabecalho();
void exibirNumerario(int numer[]);
void exibirProdutos();
void exibirEstoque();
void exibirPrecos();
int lerInts(int qtd_ler, int vetor[]);
void carregarMaquina();
void zerarNumerario(int numer[]);
void retirarNumerario(int caixa[], int troco[]);
int totalizarNumerario(int numer[]);
int trocar(const int total, int troco[]);
int fazerTroco(const int total, const unsigned int id_num_inic, int troco[]);

const int VAL_NUMERARIO[] = {1000, 500, 200, 100, 50, 25, 10, 5};

int caixa[QT_VAL_DISTINTOS];

struct produto {
	char nome[12];
	int preco;
	int qtd;   
};

struct produto estoque[PROD_TIPOS] = {
	/* {nome,preco,qtd} */
	{"Choca-Cola",0,0},
	{"Mineralba",0,0},
	{"Toblerini",0,0},
	{"Xiritos",0,0}
};

int main ()
{
	int saldo;		/* saldo do usuario em centavos */
	enum comando cmd;	/* operacao feita pelo usuario */
	int troco[QT_VAL_DISTINTOS];
	int resultado;  /* resultado de trocar */
	saldo = 0;
	cmd = ENTRAR;
	carregarMaquina();
	printf("======= Estado inicial =======\n");
	exibirCaixa();
	exibirEstoque();
	exibirPrecos();
	printf("======= Interacoes com usuarios =======\n");
	while (cmd != SAIR) {
		scanf("%d",(int*)&cmd);
		switch ( cmd ) {
			case SAIR:
				break;
			case TROCO:
				resultado = trocar(saldo, troco);
				assert(SEM_TROCO!=resultado);
				printf("troco: %d ", saldo);
				retirarNumerario(caixa, troco);
				saldo = 0;
				printf("(saldo do usuario: %d)\n",saldo);
				exibirCaixa();
				exibirEstoque();
				break;
			case CHOCA:
			case MINER:
			case TOBLE:
			case XIRIT:
				saldo = vender(saldo, cmd);
				break;
			case R10:
			case R5:
			case R2:
			case R1:
			case C50:
			case C25:
			case C10:
			case C5:
				saldo = receber(saldo, cmd);
				break;
			default:
				printf("*** OPERACAO INVALIDA ***\n");
			
		}
	}
	return 0;
}

int receber(int saldo, int valor)
{
	unsigned int i;
	for (i=0;i<QT_VAL_DISTINTOS;++i) {
		if (VAL_NUMERARIO[i]==valor) {
			caixa[i]++;
			break;
		}
	}
	saldo += valor;
	printf("recebimento de %d (saldo do usuario: %d)\n",valor,saldo);
	return saldo;
}

int vender(int saldo, enum comando botao)
{
	struct produto *produto;
	int preco;
	int resultado = 0;
	int troco[QT_VAL_DISTINTOS];
	zerarNumerario(troco);
	produto = &estoque[-botao-1];
	preco = produto->preco;
	if ( (produto->qtd > 0) &&
	     (saldo >= preco) &&
	     (SEM_TROCO != (resultado = trocar(saldo-preco,troco))) ) {
		produto->qtd--;
		saldo -= preco;
		printf("venda de %s (preco: %d, saldo do usuario: %d)\n",
			produto->nome, preco, saldo);
	} else {
		printf("venda de %s (preco: %d) nao efetuada por ",
		        produto->nome, preco);
		/* exibir todas as causas do cancelamento da venda */
		if (produto->qtd == 0) {
			printf("falta de estoque; ");
		}
		if (saldo < preco) {
			printf("saldo insuficiente; ");
		}
		if (SEM_TROCO == resultado) {
			printf("falta de troco");
		}
		printf("\n");
	}
	return saldo;
}

void exibirCaixa()
{
	printf("caixa:\n");
	exibirCabecalho();
	exibirNumerario(caixa);
}

void exibirCabecalho() {
	unsigned int i;
	int r, c; /* reais, centavos */
        printf("   ");
	for (i=0;i<QT_VAL_DISTINTOS;++i) {
		r = VAL_NUMERARIO[i] / 100;
		c = VAL_NUMERARIO[i] % 100;
		printf("R$ %d,%02d  ",r,c);
	}
	printf("\n");
}

void exibirNumerario(int numerario[]) {
	unsigned int i;
	printf("    ");
	for (i=0;i<QT_VAL_DISTINTOS;++i) {
		printf("%7d  ",numerario[i]);
	}
	printf("\n");
}

void exibirProdutos()
{
	int i;
	for (i=0;i<PROD_TIPOS;++i) {
		printf("%9s         ",estoque[i].nome);
	}
	printf("\n");
}

void exibirEstoque()
{
	int i;
	printf("estoque:\n ");
	exibirProdutos();
	printf("  ");
	for (i=0;i<PROD_TIPOS;++i) {
		printf("%9d         ",estoque[i].qtd);
	}
	printf("\n");
}

void exibirPrecos()
{
	int i;
	printf("precos em centavos:\n ");
	exibirProdutos();
	printf("  ");
	for (i=0;i<PROD_TIPOS;++i) {
		printf("%9d         ",estoque[i].preco);
	}
	printf("\n");
}

int lerInts(int qtd_ler, int vetor[])
{
	/* preenche o vetor com qtd_ler inteiros lidos de 
	   uma linha via stdin */
	char linha[80];  /* buffer de entrada */ 
	char delim[] = " \t"; /* delimtadores: branco e tab */
	char *lido = NULL;
	int qtd_lidos = 0;
	fgets(linha, sizeof(linha), stdin);
	lido = strtok(linha, delim);
	while( lido != NULL && qtd_lidos < qtd_ler) {
		vetor[qtd_lidos] = atoi(lido);
		qtd_lidos++;
		lido = strtok(NULL, delim);
	} 
	assert(qtd_ler==qtd_lidos);
	return qtd_lidos;
}

void carregarMaquina()
{
	int params[PROD_TIPOS]; /* array temp. para qtd. e precos de produtos*/
	int i;
	lerInts(QT_VAL_DISTINTOS,caixa);
	lerInts(PROD_TIPOS,params);
	for (i=0;i<PROD_TIPOS;++i) {
		estoque[i].qtd = params[i];
	}
	lerInts(PROD_TIPOS,params);
	for (i=0;i<PROD_TIPOS;++i) {
		estoque[i].preco = params[i];
	}
}

void zerarNumerario(int numerario[]) {
	unsigned int i;
	for (i=0;i<QT_VAL_DISTINTOS;++i) {
		numerario[i] = 0;
	}
}

void retirarNumerario(int caixa[], int troco[])
{
	unsigned int i;
	for (i=0;i<QT_VAL_DISTINTOS;++i) {
		assert(caixa[i]>=troco[i]);
		caixa[i] -= troco[i];
	}
}

int totalizarNumerario(int numerario[]) {
	/* calcular valor total de notas e moedas */
	unsigned int i;
	int total = 0;
	for (i=0;i<QT_VAL_DISTINTOS;++i) {
		total += VAL_NUMERARIO[i] * numerario[i];
	}
	return total;
}

int trocar(const int total, int troco[])
{
	int resultado;
	zerarNumerario(troco);
	resultado = fazerTroco(total, 0, troco);
	if (SEM_TROCO == resultado) {
		return SEM_TROCO;
	} else {
		assert(resultado==total);
		return total;
	}
}

int fazerTroco(const int total, const unsigned int id_num_inic, int troco[])
{
	unsigned int id_numerario;
	int qt_numerario, qt_num_maximo;
	int valor, resto, resultado;
	for (id_numerario=id_num_inic; id_numerario<QT_VAL_DISTINTOS; ++id_numerario) {
		valor = VAL_NUMERARIO[id_numerario];
		if (valor > total)
			continue;
		qt_num_maximo = total / valor;
		if (qt_num_maximo > caixa[id_numerario]) {
			qt_num_maximo = caixa[id_numerario];
		}
		resto = total - qt_num_maximo * valor;
		if (resto == 0) { /* encontramos o troco exato */
			troco[id_numerario] = qt_num_maximo;
			return total;
		}
		if (id_numerario==(QT_VAL_DISTINTOS-1)) {
			/* já estamos no numerário de menor valor */
			return SEM_TROCO;
		}
		/* fazer troco para o resto; reduzir qt_numerario se preciso */
		for (qt_numerario=qt_num_maximo; qt_numerario>0;--qt_numerario) {
			resultado = fazerTroco(resto, id_numerario+1, troco);
			if (resultado!=SEM_TROCO) {
				troco[id_numerario] = qt_numerario;
				return total;
			}
			/* se ainda não fechamos o troco, aumentar o resto */
			resto += valor;
		}
	}
	return SEM_TROCO;
}

